/**
 * 
 */
package com.scaffold.upload.action;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import wangjg.codec.ImageEditor;
import wangjg.commons.character.DigestUtil;
import wangjg.commons.character.Random;
import wangjg.commons.io.FileHeaderHelper;
import wangjg.commons.io.FilePathHelper;

import com.oreilly.servlet.multipart.FilePart;
import com.oreilly.servlet.multipart.MultipartParser;
import com.oreilly.servlet.multipart.ParamPart;
import com.oreilly.servlet.multipart.Part;
import com.scaffold.BasicBizWebAction;
import com.scaffold.commons.json.JacksonUtil;
import com.scaffold.commons.web.WebUtil;
import com.scaffold.upload.FileUtil;
import com.scaffold.upload.entity.FileInfo;
import com.scaffold.upload.service.FileService;

/**
 * @author wangjg
 *
 */
@Controller("upload")
public class UploadAction extends BasicBizWebAction {
	private static final int MAX_SIZE = 1024*1024*5;

	private static final String PREFIX_UPLOAD = "/file/upload";
	private static final String PREFIX_DOWN_IMG = "/file/image";
	private static final String PREFIX_DOWN_FILE = "/file/download";
	
	@Autowired
	private FileService fileService;
	
	/**
	 * 上传页面
	 * @param model
	 * @return
	 */
	@RequestMapping("/file/upfile.xhtml")
	public String uploadForm(ModelMap model){
		Map<String, String> params = this.getParameterMap();
		if(params.get("success")!=null){
			String json = new JacksonUtil().objToJson(params);
			model.put("uploaded", json);	
		}
		return "/commons/upfile.vm";
	}
	
	/**
	 * 接收上传到指定文件(一个文件)
	 */
	@RequestMapping(PREFIX_UPLOAD+"/**")
	public String uploadX(ModelMap model, HttpServletRequest request) {
		String destDir = getUploadDir(request);
		
		Map<String, String> params = new HashMap<String,String>();
		List<String> pathList = new ArrayList<String>();
		try{
			Part part = null;
			if(request.getContentLength()>MAX_SIZE){
				throw new RuntimeException("文件太大，大小限制:"+(MAX_SIZE/1024.0/1024)+" M");
			}
			MultipartParser mp = new MultipartParser(request, MAX_SIZE);
			mp.setEncoding("UTF-8");
			while ((part = mp.readNextPart()) != null) {
				if (part.isParam()) {
					ParamPart paramPart = (ParamPart) part;
					String name = paramPart.getName();
					String value = paramPart.getStringValue();
					params.put(name, value);
				} else if (part.isFile()) {
					FilePart filePart = (FilePart) part;
					//检查
					PushbackInputStream pis = new PushbackInputStream(filePart.getInputStream(), 10);
					byte[] head = new byte[8];
					int len = pis.read(head);
					String extName = FileHeaderHelper.getExtName(head);
					pis.unread(head, 0, len);
					if (extName == null) {
						request.getInputStream().close();
						throw new RuntimeException("文件类型错误！");
					}
					//检查文件是否存在
					String filePath = null;
					if("Y".equals(params.get("keepName"))){
						filePath = getSavePath(destDir, filePart);
					}else{
						filePath = getSavePath(destDir, filePart);
					}
					if(this.fileService.exists(filePath)){
						throw new RuntimeException("file exists.");
					}
					//保存文件信息
					FileInfo fileInfo = FileUtil.getFileInfo(filePart, filePath);
					this.fileService.save(fileInfo, pis);
					pathList.add(fileInfo.getFilePath());
				}
			}
		}catch(Exception e){
			params.put("error", e.getMessage());
		}
		
		if(pathList.size()>0){
			String pathStr = StringUtils.join(pathList,',');
			params.put("path", pathStr);
			params.put("vcode", DigestUtil.md5("wjg"+pathStr));
			params.put("success", "Y");
		}

		model.put("callbackUrl", params.remove("callbackUrl"));
		model.put("callbackParams", params);

		return "/commons/formForward.vm";
	}
	
	private String getSavePath(String destDir, FilePart filePart){
		String filePath = destDir+'/'+filePart.getFileName();
		return filePath;
	}
	
	private String getSavePath(String destDir, String extName){
		String newFileName = Random.getRandomStringbByTime() + '.' + extName;
		String newPath = destDir+"/"+newFileName;
		return newPath;
	}
	
	private String getUploadDir(HttpServletRequest request){
		String servletPath = request.getServletPath();
		String path = servletPath.substring(PREFIX_UPLOAD.length());
		return path;
	}
	
	@RequestMapping("/file/confirm.xhtml")
	public void move(String path, String destPath, HttpServletResponse response) throws IOException{
		this.fileService.move(path, destPath);
		response.getWriter().write(destPath);
	}

	@RequestMapping(PREFIX_DOWN_IMG+"/orig/**")
	public void getImage(HttpServletRequest request, HttpServletResponse response) throws IOException{
		this.getImage(null, null, request, response);
	}
	
	@RequestMapping(PREFIX_DOWN_IMG+"/{w}x{h}/**")
	public void getImage(@PathVariable String w, @PathVariable String h, HttpServletRequest request, HttpServletResponse response) throws IOException{
		String servletPath = request.getServletPath();
		
		String extName = FilePathHelper.getExtName(servletPath);
		if(!"jpg".equals(extName) && !"png".equals(extName) && !"gif".equals(extName)){
			return ;
		}
		response.setContentType("image/"+extName);
		WebUtil.setCacheExpireDate(response, 3600*24*365);
		
		String filePath = servletPath.substring(PREFIX_DOWN_IMG.length()+1);
		filePath = filePath.substring(filePath.indexOf('/'));
		
		OutputStream out = response.getOutputStream();
		if(w==null||w.length()==0||h==null||h.length()==0){
			Long fileLength = this.fileService.getFileLength(filePath);
			response.setContentLength(fileLength.intValue());
			
			this.fileService.load(filePath, out);
		}else{
			String size = w+'x'+h;
			String sizeFilePath = '/'+size+filePath;
			if(this.fileService.exists(sizeFilePath)){
				Long fileLength = this.fileService.getFileLength(sizeFilePath);
				response.setContentLength(fileLength.intValue());
				
				this.fileService.load(sizeFilePath, out);
				return;
			}
			
			ImageEditor imageEditor = new ImageEditor();
			InputStream in = null;
			try{
				String[] sa = size.split("x");
				int width = Integer.parseInt(sa[0]);
				int height = Integer.parseInt(sa[1]);
				
				in = this.fileService.getInputStream(filePath);
				imageEditor.read(in);
				imageEditor.zoom(width, height);
				imageEditor.write(out, extName);
				out.flush();
			}catch(Exception e){
				e.printStackTrace();
			}finally{
				if(in!=null){
					try{
						in.close();
					}catch(Exception e){
						e.printStackTrace();
					}
				}
			}
			
			OutputStream fileOut = null;
			try{
				fileOut = this.fileService.getOutputStream(sizeFilePath);
				imageEditor.write(fileOut, extName);
			}catch(Exception e){
				e.printStackTrace();
			}finally{
				try{
					if(fileOut!=null){
						fileOut.close();
					}
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}
	}
	
	@RequestMapping(PREFIX_DOWN_FILE+"/**")
	public void download(HttpServletRequest request, HttpServletResponse response) throws IOException{
		String servletPath = request.getServletPath();
		String filePath = servletPath.substring(PREFIX_DOWN_FILE.length());
		if(filePath==null||filePath.length()==0){
			return;
		}
		String fileName = FilePathHelper.getFileName(filePath);
		String extName = FilePathHelper.getExtName(fileName);
		String contentType = FileHeaderHelper.getMediaType(extName);
		WebUtil.setDownloadHeader(request, response, fileName, contentType);
		WebUtil.setCacheExpireDate(response, Integer.MAX_VALUE);
		this.fileService.load(filePath, response.getOutputStream());
	}
	
}
